import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In, Like } from 'typeorm';
import GatherMachine from '@main/entities/service/admin/gather/gather-machine.entity';
import { ApiException } from '@main/common/exceptions/api.exception';
import GatherMachineParameter from '@main/entities/service/admin/gather/gather-machine-parameter.entity';
import GatherParameter from '@main/entities/service/admin/gather/gather-parameter.entity';
import { getDFModbusReadListFn } from './handleModbusData';
import { getDFFinsReadListFn } from './handleFinsData';
import { setAddressObjFn } from './handleS7Data';
import { SysOperationLogService } from '../../system/operationLog/operationLog.service';
// import {
//   connectInitFn,
//   connectFn,
//   deleteChildProcessFn,
//   getPlcDataFn,
// } from '@main/plc/childProcess';
// import { getMainWinFn } from '@main/ipc/ipc.module';

@Injectable()
export class GatherMachineService {
  constructor(
    @InjectRepository(GatherMachine)
    private gatherMachineRepository: Repository<GatherMachine>,
    @InjectRepository(GatherMachineParameter)
    private gatherMachineParameterRepository: Repository<GatherMachineParameter>,
    @InjectRepository(GatherParameter)
    private gatherParameterRepository: Repository<GatherParameter>,
    private operationLogService: SysOperationLogService,
  ) {}

  async list() {
    const machineParameterList = await this.gatherMachineRepository.find();

    for (let i = 0; i < machineParameterList.length; i++) {
      const { accessCode } = machineParameterList[i];

      const addressList = await this.gatherMachineParameterRepository.find({
        where: {
          accessCode,
        },
      });

      machineParameterList[i]['addressList'] = addressList;
    }

    return machineParameterList;
  }

  /**
   * 罗列所有配置
   */
  async page(data) {
    const { pageSize, pageNum, entity = {} } = data;

    const { accessName, accessCode, machineCode } = entity;

    return await this.gatherMachineRepository.findAndCount({
      where: {
        accessName: Like(`%${accessName}%`),
        accessCode: Like(`%${accessCode}%`),
        machineCode: Like(`%${machineCode}%`),
      },
      order: {
        updateTime: 'DESC',
      },
      take: pageSize,
      skip: (pageNum - 1) * pageSize,
    });
  }

  /**
   * 新增
   */
  async add(data, user, mac, ip, syncHostSqlite) {
    const databaseOperationLogList = [];

    const { addressList, accessCode, machineCode } = data;

    const gatherMachine = await this.gatherMachineRepository.insert({
      ...data,
      createName: user.nickName,
      createUserName: user.userName,
      updateName: user.nickName,
      updateUserName: user.userName,
    });
    databaseOperationLogList.push({
      type: 'add',
      dataType: 'obj',
      database: 'GatherMachine',
      checkOnlyKeyList: [],
      data: gatherMachine,
      whereObj: null,
      remark: `GatherMachine表新增数据`,
    });
    await this.operationLogService.add(
      {
        type: 'add',
        msg: `连接管理模块新增连接信息`,
        databaseOperationLogList,
        remark: `新增连接${data.accessName}`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );

    if (addressList && addressList.length > 0) {
      // 关联菜单
      const insertRows = await Promise.all(
        addressList.map(async (m) => {
          const { isCreate, parameterId, parameterCode, parameterName } = m;

          if (isCreate && parameterId && parameterCode && parameterName) {
            const gatherParameter = await this.gatherParameterRepository.save({
              id: parameterId,
              parameterName,
              parameterCode,
              createName: user.nickName,
              createUserName: user.userName,
              updateName: user.nickName,
              updateUserName: user.userName,
            });
            databaseOperationLogList.push({
              type: 'add',
              dataType: 'obj',
              database: 'GatherParameter',
              checkOnlyKeyList: [],
              data: gatherParameter,
              whereObj: null,
              remark: `GatherParameter表新增数据`,
            });
            await this.operationLogService.add(
              {
                type: 'add',
                msg: `采集管理模块新增数据`,
                databaseOperationLogList,
                remark: `新增采集${data.parameterName}`,
                mac,
                ip,
                syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
              },
              user
            );
          }

          return {
            ...m,
            accessCode,
          };
        })
      );
      const gatherMachineParameter = await this.gatherMachineParameterRepository.insert(insertRows);
      databaseOperationLogList.push({
        type: 'add',
        dataType: 'obj',
        database: 'GatherMachineParameter',
        checkOnlyKeyList: [],
        data: gatherMachineParameter,
        whereObj: null,
        remark: `GatherMachineParameter表新增数据`,
      });
      await this.operationLogService.add(
        {
          type: 'add',
          msg: `采集管理模块采集配置新增数据`,
          databaseOperationLogList,
          remark: `新增采集配置${data.parameterName}`,
          mac,
          ip,
          syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
        },
        user
      );

    }

    if (this.machineParameterListObj) {
      delete this.machineParameterListObj[machineCode];

    }
  }

  /**
   * 更新
   */
  async update(data, user, mac, ip1, syncHostSqlite) {
    const databaseOperationLogList = [];
    const {
      id,
      accessName,
      accessCode,
      machineCode,
      connectType,
      connectReadTime,
      connectOverTime,
      reConnectNum,
      connectionType,
      ip,
      port,
      path,
      baudRate,
      dataBits,
      autoOpen,
      stopBits,
      parity,
      protocol,
      host,
      rack,
      slot,
      userName,
      password,
      remark,
      addressList,
      isOpenMultiLine,
    } = data;

    await this.gatherMachineRepository.update(
      { id },
      {
        accessName,
        machineCode,
        connectType,
        connectReadTime,
        connectOverTime,
        reConnectNum,
        connectionType,
        ip,
        port,
        path,
        baudRate,
        dataBits,
        autoOpen,
        stopBits,
        parity,
        protocol,
        host,
        rack,
        slot,
        userName,
        password,
        isOpenMultiLine,
        remark,
        updateName: user.nickName,
        updateUserName: user.userName,
      }
    );
    databaseOperationLogList.push({
      type: 'update',
      dataType: 'obj',
      database: 'GatherMachine',
      checkOnlyKeyList: [],
      data: {
        accessName,
        machineCode,
        connectType,
        connectReadTime,
        connectOverTime,
        reConnectNum,
        connectionType,
        ip,
        port,
        path,
        baudRate,
        dataBits,
        autoOpen,
        stopBits,
        parity,
        protocol,
        host,
        rack,
        slot,
        userName,
        password,
        isOpenMultiLine,
        remark,
        updateName: user.nickName,
        updateUserName: user.userName,
      },
      whereObj: { id },
      remark: `GatherMachine表更新连接信息`,
    });
    await this.operationLogService.add(
      {
        type: 'update',
        msg: `连接管理模块更新连接信息`,
        databaseOperationLogList,
        remark: `更新连接${data.colorName}`,
        mac,
        ip:ip1,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );

    if (addressList && addressList.length) {
      // 疑惑1
      await this.gatherMachineParameterRepository.delete({ accessCode });
      await this.gatherMachineParameterRepository.insert(
        addressList.map((item) => ({ ...item, accessCode }))
      );
    }

    if (this.machineParameterListObj) {
      delete this.machineParameterListObj[machineCode];
    }
  }

  /**
   * 删除
   */
  async delete(idList, user, mac, ip, syncHostSqlite) {
    const databaseOperationLogList = [];

    const machineParameterList = await this.gatherMachineRepository.find({
      where: {
        id: In(idList),
      },
    });

    for (let i = 0; i < machineParameterList.length; i++) {
      const { accessCode } = machineParameterList[i];
    // 疑惑1
      await this.gatherMachineParameterRepository.delete({ accessCode });
    }
    await this.gatherMachineRepository.delete(idList);

    databaseOperationLogList.push({
      type: 'delete',
      dataType: 'array',
      database: 'GatherMachine',
      checkOnlyKeyList: [],
      data: null,
      whereObj: idList,
      remark: `GatherMachine表删除连接信息`,
    });
    await this.operationLogService.add(
      {
        type: 'delete',
        msg: `连接管理模块删除连接信息`,
        databaseOperationLogList,
        remark: `删除连接信息`,
        mac,
        ip,
        syncHostSqlite: isNaN(Number(syncHostSqlite)) ? 0 : Number(syncHostSqlite),
      },
      user
    );


  }

  async isExistKey(accessCode: string) {
    const result = await this.gatherMachineRepository.findOne({ where: { accessCode } });

    if (result) {
      throw new ApiException(10030);
    }
  }

  async isExistKey2(dto) {
    const { addressList, machineCode, accessCode } = dto;

    if (addressList && addressList.length) {
      const parameterIdList = addressList.map((item) => item.parameterId);

      const resultList = await this.gatherMachineRepository.find({ where: { machineCode } });

      if (resultList && resultList.length) {
        for (let i = 0; i < resultList.length; i++) {
          if (resultList[i]['accessCode'] !== accessCode) {
            const rParameterIdList = (
              await this.gatherMachineParameterRepository.find({
                where: { accessCode: resultList[i]['accessCode'] },
              })
            ).map((item) => item.parameterId);

            const hasSame =
              parameterIdList.filter((value) => rParameterIdList.includes(value)).length > 0;

            if (hasSame) {
              throw new ApiException(10031);
            }
          }
        }
      }
    }
  }

  /**
   * 根据角色获取角色信息
   */
  async info(id) {
    const machineParameter = await this.gatherMachineRepository.findOne({
      where: { id },
    });

    if (machineParameter) {
      const { accessCode } = machineParameter;

      const addressList = await this.gatherMachineParameterRepository.find({
        where: { accessCode },
      });

      machineParameter['addressList'] = addressList;

      return machineParameter;
    }
  }

  machineParameterListObj = null;

  // connectMachineCode = null;

  async getMachineParameterListByMachineCode(machineCode) {
    let newMachineParameterList = null;

    if (this.machineParameterListObj) {
      newMachineParameterList = this.machineParameterListObj[machineCode] || null;

      if (newMachineParameterList) {
        // this.plcConnectFn(newMachineParameterList, machineCode);

        return newMachineParameterList;
      }
    }

    const machineParameterList = await this.gatherMachineRepository.find({
      where: { machineCode },
    });

    if (machineParameterList && machineParameterList.length) {
      newMachineParameterList = [];

      for (let i = 0; i < machineParameterList.length; i++) {
        const { accessCode, connectType, connectionType, accessName, isOpenMultiLine } =
          machineParameterList[i];

        const addressList = await this.gatherMachineParameterRepository.find({
          where: { accessCode },
        });

        machineParameterList[i]['plcId'] = accessCode;

        machineParameterList[i]['name'] = accessName;

        machineParameterList[i]['protocol'] = connectionType;

        if (addressList && addressList.length) {
          let list = [];

          for (const item of addressList) {
            list.push({ ...(await this.setItemFn(item, connectType, accessCode)) });
          }

          if (connectType === 'modbus') {
            list = getDFModbusReadListFn(list);

            if (connectionType !== 'Serial Port' && isOpenMultiLine === 1) {
              if (list && list.length) {
                for (const item of list) {
                  newMachineParameterList.push({
                    ...machineParameterList[i],
                    addressList: [item],
                  });
                }
              }
            }
          } else if (connectType === 'fins') {
            list = getDFFinsReadListFn(list);
          } else if (connectType === 's7') {
            machineParameterList[i]['addressObj'] = setAddressObjFn(list);
          }

          machineParameterList[i]['addressList'] = list;
        } else {
          machineParameterList[i]['addressList'] = [];
        }

        if (
          connectType !== 'modbus' ||
          (connectType === 'modbus' && connectionType === 'Serial Port') ||
          (connectType === 'modbus' && connectionType !== 'Serial Port' && isOpenMultiLine !== 1)
        ) {
          newMachineParameterList.push(machineParameterList[i]);
        }
      }

      // this.plcConnectFn(newMachineParameterList, machineCode);

      if (!this.machineParameterListObj) {
        this.machineParameterListObj = {};
      }

      this.machineParameterListObj[machineCode] = newMachineParameterList;

      return newMachineParameterList;
    }
  }

  // plcConnectFn(list, machineCode) {
  //   if (this.connectMachineCode === machineCode) {
  //     return;
  //   }

  //   const { mainWindow, ipcMain } = getMainWinFn();

  //   connectInitFn(list, mainWindow).then((res) => {
  //     const { clickFn } = res;

  //     ipcMain.removeAllListeners('plcClick');

  //     ipcMain.on('plcClick', (_event, data) => {
  //       clickFn(data);
  //     });

  //     ipcMain.removeAllListeners('plcConnect');

  //     ipcMain.on('plcConnect', (_event, _data) => {
  //       connectFn();
  //     });

  //     ipcMain.removeAllListeners('getPlcData');

  //     ipcMain.on('getPlcData', (_event, _data) => {
  //       getPlcDataFn();
  //     });
  //   });

  //   this.connectMachineCode = machineCode;
  // }

  async setItemFn(item, connectionType, accessCode) {
    const {
      parameterId,
      slaveId,
      startOffset,
      address,
      finsAddress,
      type,
      leng,
      decimalPlaces,
      byteOrder,
      s7Address,
      opcuaAddress,
      opcuaType,
      computedNum,
      computedType,
      bitAccessCodeObj: bitFunIdObj,
    } = item;

    const parameterObj = await this.gatherParameterRepository.findOne({
      where: { id: parameterId },
    });

    const { parameterCode: funId } = parameterObj;

    let obj = item;

    switch (connectionType) {
      case 'modbus':
        obj = {
          id: slaveId.toString(16).padStart(2, '0').toUpperCase(),
          offset: 0,
          startOffset: startOffset + address,
          plcId: accessCode,
          type: type === 'bool' ? 'M' : 'D',
          leng,
          list: [
            {
              code: null,
              funId,
              type,
              leng,
              decimalPlaces,
              byteOrder,
              computedType,
              computedNum,
              plcId: accessCode,
              bitFunIdObj: bitFunIdObj ? JSON.parse(bitFunIdObj) : null,
            },
          ],
        };
        break;
      case 'fins':
        let offset = 0;

        if (finsAddress.includes('W')) {
          const parts = finsAddress.split('.'); // 将finsAddress按照"."分割成两部分
          const row = parseInt(parts[0].substr(1)); // 提取第一部分并去掉前缀字母"W"，然后将其转换为整数
          const column = parseInt(parts[1]); // 提取第二部分并将其转换为整数
          const offsetIdNum = row * 16 + column; // 根据公式计算offsetIdNum

          offset = offsetIdNum;
        } else if (finsAddress.includes('D')) {
          const offsetIdNum = Number(finsAddress.replace('D', ''));

          offset = offsetIdNum;
        }

        obj = {
          plcId: accessCode,
          type: type === 'bool' ? 'M' : 'D',
          leng,
          offset,
          startOffset: 0,
          list: [
            {
              finsAddress,
              code: null,
              funId,
              type,
              leng,
              decimalPlaces,
              byteOrder,
              computedType,
              computedNum,
              plcId: accessCode,
              bitFunIdObj: bitFunIdObj ? JSON.parse(bitFunIdObj) : null,
            },
          ],
        };
        break;
      case 's7':
        obj = {
          address: s7Address,
          funId,
          plcId: accessCode,
        };
        break;
      case 'opcua':
        obj = {
          nodeId: opcuaAddress,
          funId,
          type: opcuaType,
          plcId: accessCode,
        };
        break;
      default:
        break;
    }

    return obj;
  }

  async clearMachineConnectFn() {
    this.machineParameterListObj = null;

    // this.connectMachineCode = null;

    // deleteChildProcessFn();
  }
}
